//aoio 中一些基础的接口

#include <cstddef>
#include <cstdint>
#include <iostream>
#include <vector>
#include <memory>

#include <boost/asio/buffer.hpp>
#include <boost/asio/io_context.hpp>

#include "../log.h"
#include "sync_api.h"


//客户端生成端点
int client_end_point()
{
    std::string raw_ip_address = "127.0.0.1";
    uint16_t port = 8080;

    boost::system::error_code ec; //boost中的错误码
    //从点分十进制形式的 IPv4 地址字符串 或 十六进制表示法的 IPv6 地址创建地址。
    boost::asio::ip::address ip_address = boost::asio::ip::address::from_string(raw_ip_address, ec);
    log_debug("client ipv4 address: {}", ip_address.to_string());
    if(ec.value() != 0)
    {
        log_error("ip address error, error code: {}, message: {}", ec.value(), ec.message());
        return ec.value();
    }

    //创建tcp端点 - 初始化tcp scoket
    boost::asio::ip::tcp::endpoint ep(ip_address, port);
    return 0;
}

//客户端通过ip连接服务器
int connect_to_server()
{
    std::string raw_ip_address = "127.0.0.1";
    uint16_t port = 8080;
    try
    {
        boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string(raw_ip_address), port);
        boost::asio::io_context ios;
        boost::asio::ip::tcp::socket socket(ios, ep.protocol()); //默认ipv4
        socket.connect(ep);
    }
    catch (boost::system::system_error const & e)
    {
        log_error("connect to server error, error code: {}, message: {}", e.code().value(), e.what());
        return e.code().value();
    }
    return 0;
}

//客户端通过域名连接服务器
int dns_connect_to_end()
{
    std::string host = "www.baidu.com";
    uint16_t port = 80;
    boost::asio::io_context ios;
    boost::asio::ip::tcp::resolver::query query(host, std::to_string(port),
         boost::asio::ip::tcp::resolver::query::numeric_service);
    boost::asio::ip::tcp::resolver resolver(ios);
    try
    {
        boost::asio::ip::tcp::resolver::iterator it = resolver.resolve(query);
        boost::asio::ip::tcp::resolver::iterator end;
        for(; it != end; ++it)
        {
            boost::asio::ip::tcp::endpoint ep = *it;
            log_debug("host: {}, port: {}", ep.address().to_string(), ep.port());
        }
        boost::asio::ip::tcp::socket socket(ios);
        //尝试连接该范围内的第一个可连接的端点。如果连接失败（比如因为连接超时、目标不可达等）,会自动尝试连接范围内的下一个端点，直到成功连接或遍历完所有端点。
        boost::asio::connect(socket, it, end);
    }
    catch (boost::system::system_error const & e)
    {
        log_error("connect to server error, error code: {}, message: {}", e.code().value(), e.what());
        return e.code().value();
    }
    return 0;
}


//==============================================================================================================


//服务端生成端点
int server_end_point()
{
    uint16_t port = 8080;
    boost::asio::ip::address ip_address = boost::asio::ip::address_v4::any();
    log_debug("server ipv4 address: {}", ip_address.to_string());
    boost::asio::ip::address ip_addressV6 = boost::asio::ip::address_v6::any();
    log_debug("server ipv6 address: {}", ip_addressV6.to_string());

    boost::asio::ip::tcp::endpoint ep(ip_address, port);
    log_debug("server port: {}", ep.port());
    return 0;
}

//创建tcp socket
int create_tcp_socket()
{
    boost::asio::io_context ioc; //io上下文
    boost::asio::ip::tcp protocl = boost::asio::ip::tcp::v4();
    boost::asio::ip::tcp::socket socket(ioc, protocl); //第二个参数默认为ipV4
    log_debug("socket created, port: {}", socket.local_endpoint().port());
    log_debug("socket created, ip: {}", socket.local_endpoint().address().to_string());

    //当前新版本的boost中，socket的open方法已经废弃,创建好socket后会自动打开
    // boost::system::error_code ec;
    // socket.open(protocl, ec);
    // if(ec.value() != 0)
    // {
    //     log_error("socket open error, error code: {}, message: {}", ec.value(), ec.message());
    //     return ec.value();
    // }
    return 0;
}

//生成acceptor(接收器)的socket
int create_acceptor_socket()
{
    using namespace boost::asio;
    io_context ios;
    uint16_t port = 8080;

    //老版本
    ip::tcp::endpoint ep(ip::address_v4::any(), port);
    ip::tcp::acceptor accepotr(ios, ep.protocol());
    accepotr.bind(ep); //需要手动绑定
    boost::system::error_code ec;
    accepotr.is_open() ? log_debug("acceptor is open") : log_debug("acceptor is not open");//新版默认会自动打开
    // accepotr.open(ep.protocol(), ec);
    // if(ec.value() != 0)
    // {
    //     log_error("acceptor open error, error code: {}, message: {}", ec.value(), ec.message());
    //     return ec.value();
    // }
    log_debug("old acceptor created, port: {}", accepotr.local_endpoint().port());
    log_debug("old acceptor created, ip: {}", accepotr.local_endpoint().address().to_string());
    accepotr.close(); //关闭acceptor

    //------------------------------------------------------------------------------------------------------
    
    //新版本，创建acceptor后自动绑定,直接传入端点
    ip::tcp::acceptor a(ios, ip::tcp::endpoint(ip::tcp::v4(), port));
    log_debug("new acceptor created, port: {}", a.local_endpoint().port());
    log_debug("new acceptor created, ip: {}", a.local_endpoint().address().to_string());

    return 0;
}


//服务器端监听
int accept_new_connection()
{
    constexpr uint32_t BACKLOGSIZE = 500;
    uint16_t port = 8080;
    boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address_v4::any(), port);
    boost::asio::io_context ios;
    try
    {
        boost::asio::ip::tcp::acceptor acceptor(ios, ep.protocol());
        acceptor.bind(ep);
        acceptor.listen(BACKLOGSIZE);

        //用于接收连接后的socket
        boost::asio::ip::tcp::socket socket(ios);
        acceptor.accept(socket);
    }
    catch (boost::system::system_error const & e)
    {
        log_error("connect to server error, error code: {}, message: {}", e.code().value(), e.what());
        return e.code().value();
    }
    return 0;
}

//==============================================================================================================

//手动构造一个boost中的buffer
void use_const_buffer()
{
    std::string buf = "hello world";
    boost::asio::const_buffer buffer(buf.c_str(), buf.size());
    std::vector<boost::asio::const_buffer> buffers;
    buffers.emplace_back(buffer);
    //这时，buffers就可以作为参数传递给boost库中的write方法
}

//使用boost中的buffer
void use_buffer_str()
{
    std::string str = "hello world";
    boost::asio::const_buffers_1 const_buf = boost::asio::buffer((const std::string)str);
    boost::asio::mutable_buffers_1 mutable_buf(boost::asio::buffer(str));
}

//给boost中的buffer传递C风格字符串
void use_buffer_array()
{
    constexpr uint32_t BUF_SIZE = 1024;
    char* c_buf = new char[BUF_SIZE];
    memset(c_buf, 0, BUF_SIZE);
    memcpy(c_buf, "hello world", 12);
    auto input_cbuf = boost::asio::buffer(c_buf, BUF_SIZE);
    delete [] c_buf;

    //可以使用智能指针管理内存
    std::unique_ptr<char[]> u_buf(new char[BUF_SIZE]);
    memset(u_buf.get(), 0, BUF_SIZE);
    memcpy(u_buf.get(), "hello world", 12);
    // auto input_ubuf = boost::asio::buffer(static_cast<void*>(u_buf.get()), BUF_SIZE);
    auto input_ubuf = boost::asio::buffer(u_buf.get(), BUF_SIZE);
}

//同步方式发送数据
void write_some_to_socket(boost::asio::ip::tcp::socket& socket)
{
    std::string buf = "hello world";
    size_t bytes = 0;
    while(bytes != buf.length())
    {
        bytes += socket.write_some(boost::asio::buffer(buf.c_str() + bytes, 
            buf.length() - bytes));
    }
}

//同步方式发送数据
int send_to_socket(boost::asio::ip::tcp::socket& socket)
{
    std::string buf = "hello world";
    //send函数不同于write_some函数，send函数会阻塞直到发送完成后才返回
    int ret = socket.send(boost::asio::buffer(buf.c_str(), buf.length()));
    if(ret < 0)
    {
        log_error("send data error, error code: {}", ret);
        return ret;
    }
    if(ret == 0)
    {
        log_debug("The peer closes the connection");
        return ret;
    }
    return ret;
}

//同步方式发送数据
int write_to_socket(boost::asio::ip::tcp::socket& socket)
{
    std::string buf = "hello world";
    //boost::asio::write函数会阻塞直到发送完成后才返回
    int ret = boost::asio::write(socket, boost::asio::buffer(buf.c_str(), buf.length()));
    if(ret < 0)
    {
        log_error("write data error, error code: {}", ret);
        return ret;
    }
    if(ret == 0)
    {
        log_debug("The peer closes the connection");
        return ret;
    }
    return ret;
}

//同步方式读数据
std::string read_some_from_socket(boost::asio::ip::tcp::socket& socket)
{
    constexpr uint32_t BUF_SIZE = 12;
    char buf[BUF_SIZE] = {0};
    size_t bytes = 0;
    while(bytes != BUF_SIZE)
    {
        bytes += socket.read_some(boost::asio::buffer(buf + bytes, 
            BUF_SIZE - bytes));
    }

    return std::string(buf, bytes);
}

//同步方式读数据
int receive_from_socket(boost::asio::ip::tcp::socket& socket, std::string &str_buf)
{
    constexpr uint32_t BUF_SIZE = 12;
    char buf[BUF_SIZE] = {0};
    int ret = socket.receive(boost::asio::buffer(buf, BUF_SIZE));
    if(ret < 0)
    {
        log_error("receive data error, error code: {}", ret);
        return ret;
    }
    if(ret == 0)
    {
        log_debug("The peer closes the connection");
        return ret;
    }
    str_buf = std::string(buf, ret);
    return ret;
}

//同步方式读数据
int read_from_socket(boost::asio::ip::tcp::socket& socket, std::string &str_buf)
{
    constexpr uint32_t BUF_SIZE = 12;
    char buf[BUF_SIZE] = {0};
    int ret = boost::asio::read(socket, boost::asio::buffer(buf, BUF_SIZE));
    if(ret < 0)
    {
        log_error("read data error, error code: {}", ret);
        return ret;
    }
    if(ret == 0)
    {
        log_debug("The peer closes the connection");
        return ret;
    }
    str_buf = std::string(buf, ret);
    return ret;
}

//客户端同步发送数据流程
int client_send_data()
{
    std::string ip = "127.0.0.1";
    uint16_t port = 8080;
    try
    {
        boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string(ip), port);
        boost::asio::io_context ioc;
        boost::asio::ip::tcp::socket socket(ioc, ep.protocol());
        socket.connect(ep);
        write_some_to_socket(socket);
        // send_to_socket(socket);
        // write_to_socket(socket);
    }
    catch (boost::system::system_error const & e)
    {
        log_error("connect to server error, error code: {}, message: {}", e.code().value(), e.what());
        return e.code().value();
    }
    return 0;
}

//客户端同步读数据流程
int client_read_data()
{
    std::string ip = "127.0.0.1";
    uint16_t port = 8080;
    try
    {
        boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string(ip), port);
        boost::asio::io_context ioc;
        boost::asio::ip::tcp::socket socket(ioc, ep.protocol());
        socket.connect(ep);
        read_some_from_socket(socket);
        // send_to_socket(socket);
        // write_to_socket(socket);
    }
    catch (boost::system::system_error const & e)
    {
        log_error("connect to server error, error code: {}, message: {}", e.code().value(), e.what());
        return e.code().value();
    }
    return 0;
}